const yapi = require('../yapi.js');
const projectModel = require('../models/project.js');
const interfaceModel = require('../models/interface.js');
const mockExtra = require('../../common/mock-extra.js');
const {schemaValidator} = require('../../common/utils.js');
const _ = require('underscore');
const Mock = require('mockjs');
const variable = require('../../client/constants/variable.js')
const request = require("request");

/**
 *
 * @param {*} apiPath /user/tom
 * @param {*} apiRule /user/:username
 */
function matchApi(apiPath, apiRule) {
    let apiRules = apiRule.split('/');
    let apiPaths = apiPath.split('/');
    let pathParams = {
        __weight: 0
    };

    if (apiPaths.length !== apiRules.length) {
        return false;
    }
    for (let i = 0; i < apiRules.length; i++) {
        if (apiRules[i]) {
            apiRules[i] = apiRules[i].trim();
        } else {
            continue;
        }
        if (
            apiRules[i].length > 2 &&
            apiRules[i][0] === '{' &&
            apiRules[i][apiRules[i].length - 1] === '}'
        ) {
            pathParams[apiRules[i].substr(1, apiRules[i].length - 2)] = apiPaths[i];
        } else if (apiRules[i].indexOf(':') === 0) {
            pathParams[apiRules[i].substr(1)] = apiPaths[i];
        } else if (
            apiRules[i].length > 2 &&
            apiRules[i].indexOf('{') > -1 &&
            apiRules[i].indexOf('}') > -1
        ) {
            let params = [];
            apiRules[i] = apiRules[i].replace(/\{(.+?)\}/g, function (src, match) {
                params.push(match);
                return '([^\\/\\s]+)';
            });
            apiRules[i] = new RegExp(apiRules[i]);
            if (!apiRules[i].test(apiPaths[i])) {
                return false;
            }

            let matchs = apiPaths[i].match(apiRules[i]);

            params.forEach((item, index) => {
                pathParams[item] = matchs[index + 1];
            });
        } else {
            if (apiRules[i] !== apiPaths[i]) {
                return false;
            } else {
                pathParams.__weight++;
            }
        }
    }
    return pathParams;
}

function parseCookie(str) {
    if (!str || typeof str !== 'string') {
        return str;
    }
    if (str.split(';')[0]) {
        let c = str.split(';')[0].split('=');
        return {name: c[0], value: c[1] || ''};
    }
    return null;
}

function handleCorsRequest(ctx) {
    let header = ctx.request.header;
    ctx.set('Access-Control-Allow-Origin', header.origin);
    ctx.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, HEADER, PATCH, OPTIONS');
    ctx.set('Access-Control-Allow-Headers', header['access-control-request-headers']);
    ctx.set('Access-Control-Allow-Credentials', true);
    ctx.set('Access-Control-Max-Age', 1728000);
    ctx.body = 'ok';
}

// 必填字段是否填写好
function mockValidator(interfaceData, ctx) {
    let i,
        j,
        l,
        len,
        noRequiredArr = [];
    let method = interfaceData.method.toUpperCase() || 'GET';
    // query 判断
    for (i = 0, l = interfaceData.req_query.length; i < l; i++) {
        let curQuery = interfaceData.req_query[i];
        if (curQuery && typeof curQuery === 'object' && curQuery.required === '1') {
            if (!ctx.query[curQuery.name]) {
                noRequiredArr.push(curQuery.name);
            }
        }
    }
    // form 表单判断
    if (variable.HTTP_METHOD[method].request_body && interfaceData.req_body_type === 'form') {
        for (j = 0, len = interfaceData.req_body_form.length; j < len; j++) {
            let curForm = interfaceData.req_body_form[j];
            if (curForm && typeof curForm === 'object' && curForm.required === '1') {
                if (
                    ctx.request.body[curForm.name] ||
                    (ctx.request.body.fields && ctx.request.body.fields[curForm.name]) ||
                    (ctx.request.body.files && ctx.request.body.files[curForm.name])
                ) {
                    continue;
                }

                noRequiredArr.push(curForm.name);
            }
        }
    }
    let validResult;
    // json schema 判断
    if (variable.HTTP_METHOD[method].request_body && interfaceData.req_body_type === 'json' && interfaceData.req_body_is_json_schema === true) {
        const schema = yapi.commons.json_parse(interfaceData.req_body_other);
        const params = yapi.commons.json_parse(ctx.request.body);
        validResult = schemaValidator(schema, params);
    }
    if (noRequiredArr.length > 0 || (validResult && !validResult.valid)) {
        let message = `错误信息：`;
        message += noRequiredArr.length > 0 ? `缺少必须字段 ${noRequiredArr.join(',')}  ` : '';
        message += validResult && !validResult.valid ? `schema 验证请求参数 ${validResult.message}` : '';

        return {
            valid: false,
            message
        };
    }

    return {valid: true};
}

const PRE_REQUEST_CODE = [404, 504];
function preRequest(ctx) {
    let path = ctx.path;
    let header = ctx.request.header;
    const path_arr = path.split('/');
    const project_id = path_arr[2];
    path_arr.splice(0, 3);
    const rePath = path_arr.join('/');
    const getServerAddressUrl = 'http://' + header.host + '/api/project/get?id=' + project_id;
    return new Promise((resolve) => {
        request.get({
            url: getServerAddressUrl,
            json: true,
            headers: {'Cookie': header.cookie}
        }, function (err, response, serverInfo) {
            if (serverInfo && serverInfo.errcode === 0) {
                const {data: {env}} = serverInfo;
                const domain = env[0].domain;
                const reUrl = domain + '/' + rePath;
                const method = ctx.method;
                request(
                    reUrl,
                    {
                        method: method,
                        json: true,
                        headers: {...header}
                    }, (err, response, serverRes) => {
                        if (serverRes && PRE_REQUEST_CODE.includes(serverRes.status)) {
                            // 使用mock数据
                            resolve(false);
                        } else {
                            // 使用服务段返回的数据，不论对错
                            resolve(serverRes);
                        }
                    }
                );
            } else {
                resolve(false);
            }
        });
    })
}

module.exports = async (ctx, next) => {
    // no used variable 'hostname' & 'config'
    // let hostname = ctx.hostname;
    // let config = yapi.WEBCONFIG;
    let path = ctx.path;
    let header = ctx.request.header;

    if (path.indexOf('/mock/') !== 0) {
        if (next) await next();
        return true;
    }

    let paths = path.split('/');
    let projectId = paths[2];
    paths.splice(0, 3);
    path = '/' + paths.join('/');

    ctx.set('Access-Control-Allow-Origin', header.origin);
    ctx.set('Access-Control-Allow-Credentials', true);

    // ctx.set('Access-Control-Allow-Origin', '*');

    if (!projectId) {
        return (ctx.body = yapi.commons.resReturn(null, 400, 'projectId不能为空'));
    }

    let projectInst = yapi.getInst(projectModel),
        project;
    try {
        project = await projectInst.get(projectId);
    } catch (e) {
        return (ctx.body = yapi.commons.resReturn(null, 403, e.message));
    }

    if (!project) {
        return (ctx.body = yapi.commons.resReturn(null, 400, '不存在的项目'));
    }

    let interfaceData, newpath;
    let interfaceInst = yapi.getInst(interfaceModel);

    try {
        newpath = path.substr(project.basepath.length);
        interfaceData = await interfaceInst.getByPath(project._id, newpath, ctx.method);
        let queryPathInterfaceData = await interfaceInst.getByQueryPath(project._id, newpath, ctx.method);
        //处理query_path情况  url 中有 ?params=xxx
        if (!interfaceData || interfaceData.length != queryPathInterfaceData.length) {

            let i,
                l,
                j,
                len,
                curQuery,
                match = false;
            for (i = 0, l = queryPathInterfaceData.length; i < l; i++) {
                match = false;
                let currentInterfaceData = queryPathInterfaceData[i];
                curQuery = currentInterfaceData.query_path;
                if (!curQuery || typeof curQuery !== 'object' || !curQuery.path) {
                    continue;
                }
                for (j = 0, len = curQuery.params.length; j < len; j++) {
                    if (ctx.query[curQuery.params[j].name] !== curQuery.params[j].value) {
                        continue;
                    }
                    if (j === len - 1) {
                        match = true;
                    }
                }

                if (match) {
                    interfaceData = [currentInterfaceData];
                    break;
                }
                // if (i === l - 1) {
                //   interfaceData = [];
                // }

            }
        }
        //处理动态路由
        if (!interfaceData || interfaceData.length === 0) {
            let newData = await interfaceInst.getVar(project._id, ctx.method);

            let findInterface;
            let weight = 0;
            _.each(newData, item => {
                let m = matchApi(newpath, item.path);
                if (m !== false) {
                    if (m.__weight >= weight) {
                        findInterface = item;
                    }
                    delete m.__weight;
                    ctx.request.query = Object.assign(m, ctx.request.query);
                    return true;
                }
                return false;
            });

            if (!findInterface) {
                //非正常跨域预检请求回应
                if (ctx.method === 'OPTIONS' && ctx.request.header['access-control-request-method']) {
                    return handleCorsRequest(ctx);
                }

                return (ctx.body = yapi.commons.resReturn(
                    null,
                    404,
                    `不存在的api, 当前请求path为 ${newpath}， 请求方法为 ${
                        ctx.method
                        } ，请确认是否定义此请求。`
                ));
            }
            interfaceData = [await interfaceInst.get(findInterface._id)];
        }

        if (interfaceData.length > 1) {
            return (ctx.body = yapi.commons.resReturn(null, 405, '存在多个api，请检查数据库'));
        } else {
            interfaceData = interfaceData[0];
        }

        // 必填字段是否填写好
        if (project.strice) {
            const validResult = mockValidator(interfaceData, ctx);
            if (!validResult.valid) {
                return (ctx.body = yapi.commons.resReturn(
                    null,
                    404,
                    `接口字段验证不通过, ${validResult.message}`
                ));
            }
        }

        let res;
        // mock 返回值处理
        res = interfaceData.res_body;
        try {
            if (interfaceData.res_body_type === 'json') {
                if (interfaceData.res_body_is_json_schema === true) {
                    //json-schema
                    const schema = yapi.commons.json_parse(interfaceData.res_body);
                    res = yapi.commons.schemaToJson(schema, {
                        alwaysFakeOptionals: true
                    });
                } else {
                    // console.log('header', ctx.request.header['content-type'].indexOf('multipart/form-data'))
                    // 处理 format-data

                    if (
                        _.isString(ctx.request.header['content-type']) &&
                        ctx.request.header['content-type'].indexOf('multipart/form-data') > -1
                    ) {
                        ctx.request.body = ctx.request.body.fields;
                    }
                    // console.log('body', ctx.request.body)

                    res = mockExtra(yapi.commons.json_parse(interfaceData.res_body), {
                        query: ctx.request.query,
                        body: ctx.request.body,
                        params: Object.assign({}, ctx.request.query, ctx.request.body)
                    });
                    // console.log('res',res)
                }
                try {
                    res = Mock.mock(res);
                } catch (e) {
                    console.log('err', e.message);
                    yapi.commons.log(e, 'error');
                }
            }

            let context = {
                projectData: project,
                interfaceData: interfaceData,
                ctx: ctx,
                mockJson: res,
                resHeader: {},
                httpCode: 200,
                delay: 0
            };

            if (project.is_mock_open && project.project_mock_script) {
                // 项目层面的mock脚本解析
                let script = project.project_mock_script;
                yapi.commons.handleMockScript(script, context);
            }
            await yapi.emitHook('mock_after', context);

            let handleMock = new Promise(resolve => {
                setTimeout(() => {
                    resolve(true);
                }, context.delay);
            });
            await handleMock;
            if (context.resHeader && typeof context.resHeader === 'object') {
                for (let i in context.resHeader) {
                    let cookie;
                    if (i === 'Set-Cookie') {
                        if (context.resHeader[i] && typeof context.resHeader[i] === 'string') {
                            cookie = parseCookie(context.resHeader[i]);
                            if (cookie && typeof cookie === 'object') {
                                ctx.cookies.set(cookie.name, cookie.value, {
                                    maxAge: 864000000,
                                    httpOnly: false
                                });
                            }
                        } else if (context.resHeader[i] && Array.isArray(context.resHeader[i])) {
                            context.resHeader[i].forEach(item => {
                                cookie = parseCookie(item);
                                if (cookie && typeof cookie === 'object') {
                                    ctx.cookies.set(cookie.name, cookie.value, {
                                        maxAge: 864000000,
                                        httpOnly: false
                                    });
                                }
                            });
                        }
                    } else {
                        ctx.set(i, context.resHeader[i]);
                    }
                }
            }
            await preRequest(ctx).then(trueRes => {
                if (trueRes) {
                    context.mockJson = trueRes
                }
            });
            ctx.status = context.httpCode;
            ctx.body = context.mockJson;
            return;
        } catch (e) {
            yapi.commons.log(e, 'error');
            return (ctx.body = {
                errcode: 400,
                errmsg: '解析出错，请检查。Error: ' + e.message,
                data: null
            });
        }
    } catch (e) {
        yapi.commons.log(e, 'error');
        return (ctx.body = yapi.commons.resReturn(null, 409, e.message));
    }
};
